package com.gaotu.demo.jvm;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * -XX:MetaspaceSize=16M -XX:MaxMetaspaceSize=16M
 */
@RequestMapping("metaspace")
@RestController
public class MetaspaceController {
    private final List<Object> data = new ArrayList<>();

    @RequestMapping("normal/{loop}")
    public String normal(@PathVariable("loop") int loop){
        List<Object> data = new ArrayList<>();
        for (int i=0; i< loop; i++){
            data.add(createData());
        }
        return "SUCCESS";
    }

    @RequestMapping("oom/{loop}")
    public String oom(@PathVariable("loop") int loop){
        for (int i=0; i<loop; i++){
            this.data.add(createData());
        }

        return "SUCCESS";
    }

    private Object createData(){
        try {
            Random random = new Random();
            String jar = "file:" + random.nextInt() + ".jar";
            URL[] urls = new URL[]{new URL(jar)};
            URLClassLoader newClassLoader = new URLClassLoader(urls);

            Factory t = (Factory) Proxy.newProxyInstance(newClassLoader,
                    new Class<?>[]{Factory.class},
                    new MetaspaceFacadeInvocationHandler(new FactoryImpl()));
            return t;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }



    public static interface Factory{
        Object create();
    }

    public static class FactoryImpl implements Factory{

        @Override
        public Object create() {
            return null;
        }
    }
    public static class MetaspaceFacadeInvocationHandler implements InvocationHandler {
        private Factory impl;
        public MetaspaceFacadeInvocationHandler(Factory impl) {
            this.impl = impl;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(impl, args);
        }
    }
}
